<html>

<head>
  <title>Hls.js 长效缓存</title>
</head>

<body>
  <script src="./hls.js"></script>
  <video height="600" id="video" controls></video>
  
  <script>
    var video = document.getElementById('video');
    const origin_url = '//yunqivedio.alicdn.com/2017yq/v2/0x0/96d79d3f5400514a6883869399708e11/96d79d3f5400514a6883869399708e11.m3u8'

    function fileToBuf(file){
      var fr = new FileReader();
      var filename = file.name;
      let _resolve
      const ins = new Promise(resolve => {
        _resolve = resolve
      }) 
      function fn(e)  {
        var buf = e.target.result;
        _resolve(buf)
        fr.removeEventListener("loadend",fn, false)
      }
      fr.addEventListener("loadend",fn, false)
      fr.readAsArrayBuffer(file)

      return ins
    }
    class Opfs {
      constructor() {
      }
      VIDEO_FOLDER = null
      COLL_FOLDER = null // 视频流集合
      async init(video_id) {
        const opfsRoot = await navigator.storage.getDirectory()
        this.VIDEO_FOLDER = await opfsRoot.getDirectoryHandle('VIDEO_FOLDER', 
          { create: true }
        )
        this.COLL_FOLDER = await this.VIDEO_FOLDER.getDirectoryHandle(video_id, 
          { create: true }
        )
      }

      saveFile(folderName, file) {
        
      }

      async logSubper() {
        const estimateIns = await navigator.storage.estimate()
        console.log('navigator.storage.estimate: ', {
          '占用率':`${(estimateIns.usage/estimateIns.quota * 100).toFixed(2)}%`,
          '已使用': Math.floor(estimateIns.usage/1000/1000) + 'MB',
          '总容量': Math.floor(estimateIns.quota/1000/1000) + 'MB',
        })
      }

      async saveFragment(key, content) {
        const fileHandle = await this.COLL_FOLDER.getFileHandle(key,  { create: true } )
        const writable = await fileHandle.createWritable(); // 获取一个可写流
        await writable.write(content);  // 将文件内容写入数据流
        await writable.close(); // 关闭数据流，保存内容
        this.logSubper()
      }

      async getFile(key) {
        const fileHandle = await this.COLL_FOLDER.getFileHandle(key).catch(err => {})
        const file = await fileHandle.getFile()
        const content = key.endsWith('m3u8') ? await file.text() : await fileToBuf(file)
        return content
      }
      async checkHas(key) {
        const fileHandle = await this.COLL_FOLDER.getFileHandle(key).catch(err => {})
        return !!fileHandle
      }
  }

  const opfsIns = new Opfs()

  class CustomLoader extends Hls.DefaultConfig.loader {
    constructor(config) {
      super(config);
    }
    async load (context, config, callbacks) {
      const key = getUrlFileName(context.url)
      const _context = context
      const ins = this

      const hasCache = await opfsIns.checkHas(key)

      let _response
      console.log('hasCache', hasCache)
      if(hasCache) {
        const data = await opfsIns.getFile(key)
        _response = {
          code: 200,
          data,
          url: context.url,
        }
      } else {
        const res = await fetch(context.url)
        const data = context.url.endsWith('m3u8') ? await res.text() : await res.arrayBuffer()
        _response = {
          code: res.code,
          data,
          url: context.url,
        }
        opfsIns.saveFragment(key, new File([data], key))
      }
      callbacks.onSuccess(_response, ins.stats, _context);
    }
  }

    
    function getUrlFileName(url) {
      return url.split('/').pop()
    }

    async function core() {
      await opfsIns.init(getUrlFileName(origin_url))
      var hls = new Hls({
        // debug: true,
        loader: CustomLoader
      });
      hls.loadSource(origin_url);
      hls.attachMedia(video);
      hls.on(Hls.Events.MEDIA_ATTACHED, function () {
        video.muted = true;
        video.play();
        // setTimeout(()=>{
        //   video.playbackRate = 16
        // }, 100)
      });
      window._hls = hls
    }
    core()
  </script>
</body>

</html>